# Navicat keygen - 注册机是怎么工作的？

## 1. 关键词解释.

* __Navicat激活公钥__

  这是一个2048位的RSA公钥，Navicat使用这个公钥来完成相关激活信息的加密和解密。

  这个公钥储存在

  ```
  Navicat Premium.app/Contents/Resources/rpk
  ```

  中，你可以用任何一种文本编辑器打开并查看它。这个公钥的具体内容为：

  ```
  -----BEGIN PUBLIC KEY-----  
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I  
  qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv  
  a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF  
  R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2  
  WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt  
  YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ  
  awIDAQAB  
  -----END PUBLIC KEY-----  
  ```

  如果您有相应的私钥并乐意公开的话欢迎联系我，我将非常感谢您的慷慨。

  __注意：__

  从 __Navicat Premium for Mac 12.0.24__ 开始，公钥不再存储在 
  
  ```
  Navicat Premium.app/Contents/Resources/rpk
  ```
  
  中。事实上，公钥放在了Navicat的二进制执行文件
  
  ```
  Navicat Premium.app/Contents/MacOS/Navicat Premium
  ```
  
  中，你可以通过搜索`"-----BEGIN PUBLIC KEY-----"`来找到它。

  __注意：__

  从 __Navicat Premium for Mac 12.1.14__ 开始，公钥仍然以明文的方式储存在二进制可执行文件中。

  但是Navicat并不从这个明文中加载公钥。事实上，密钥是从一段0x188字节长的密文中加载的。密文为：

  ```c
  const uint8_t ciphertext[0x188] = {
      0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
      0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
      0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
      0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
      0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
      0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
      0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
      0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
      0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
      0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
      0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
      0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
      0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
      0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
      0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
      0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
      0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
      0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
      0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
      0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
      0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
      0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
      0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
      0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
      0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
  };
  ```

  这个密文是采用XOR加密得来，XOR密钥为

  ```
  \xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba
  ```

* __请求码__

  这是一个Base64编码的字符串，代表的是长度为256字节的数据。这256字节的数据是 __离线激活信息__ 用 __Navicat激活公钥__ 加密的密文。

* __离线激活请求信息__

  这是一个JSON风格的UTF-8字符串。它包含了3个Key：`"K"`、`"DI"`和`"P"`，分别代表 __序列号__、__设备识别码__（与你的电脑硬件信息相关）和 __平台__ (其实就是操作系统类型)。

  例如：  

  ```  
  { "K": "xxxxxxxxxxxxxxxx", "P": "Mac 10.13", "DI": "xxxxxxxxxxxxxxxxxxxx" }
  ```

* __激活码__

  这是一个Base64编码的字符串，代表的是长度为256字节的数据。这256字节的数据是 __离线激活回复信息__ 用 __Navicat激活私钥__ 加密的密文。目前我们不知道官方的 __Navicat激活私钥__，所以我们得替换掉软件里的公钥。

* __离线激活回复信息__

  和 __离线激活请求信息__ 一样，它也是一个JSON风格的UTF-8字符串。但是它包含5个Key，分别为`"K"`、`"N"`、`"O"`、`"T"` 和 `"DI"`.

  `"K"` 和 `"DI"` 的意义与 __离线激活请求信息__ 中的相同，且Value必须与 __离线激活请求信息__ 中的相同。

  `"N"`、`"O"`、`"T"` 分别代表 __注册名__、__组织__、__授权时间__。
  
  __注册名__ 和 __组织__ 的值类型为UTF-8编码的字符串。__授权时间__ 的值类型可以为字符串或整数（感谢@Wizr在issue #10中的报告）。

  和Windows版本Navicat不同的是，`"T"` 项不可以省略，并且和当前时间的差距必须在-1 ~ +4天之内。

  例如：

  ```
  {  
    "DI" : "xxxxxxxxxxxxxxxxxxxx",  
    "T" : "1515770827.925012",  
    "K" : "xxxxxxxxxxxxxxxx",  
    "N" : "DoubleLabyrinth",  
    "O" : "Shadow"  
  }
  ```

* __序列号__

  这是一个被分为了4个部分的字符串，其中每个部分都是4个字符长。

  __序列号__ 是通过10个字节的数据来生成的。为了表达方便，我用 __uint8_t data[10]__ 来表示这10个字节。

  1. __data[0]__ 和 __data[1]__ 必须分别为 `0x68` 和 `0x2A`。

     这两个字节为Navicat的标志数。

  2. __data[2]__、__data[3]__ 和 __data[4]__ 可以是任意字节，你想设成什么都行。

  3. __data[5]__ 和 __data[6]__ 是Navicat的语言标志，值如下：

     |  语言类型   |  data[5]  |  data[6]  |  发现者         |
     |------------|:---------:|:---------:|-----------------|
     |  English   |  0xAC     |  0x88     |                 |
     |  简体中文   |  0xCE     |  0x32     |                 |
     |  繁體中文   |  0xAA     |  0x99     |                 |
     |  日本語     |  0xAD     |  0x82     |  @dragonflylee  |
     |  Polski    |  0xBB     |  0x55     |  @dragonflylee  |
     |  Español   |  0xAE     |  0x10     |  @dragonflylee  |
     |  Français  |  0xFA     |  0x20     |  @Deltafox79    |
     |  Deutsch   |  0xB1     |  0x60     |  @dragonflylee  |
     |  한국어     |  0xB5     |  0x60     |  @dragonflylee  |
     |  Русский   |  0xEE     |  0x16     |  @dragonflylee  |
     |  Português |  0xCD     |  0x49     |  @dragonflylee  |

  4. __data[7]__ 是Navicat产品ID。（感谢 @dragonflylee 和 @Deltafox79提供的数据）

     |产品名                |Enterprise|Standard|Educational|Essentials|
     |---------------------|:--------:|:------:|:---------:|:--------:|
     |Navicat Report Viewer|0x0B      |        |           |          |
     |Navicat Data Modeler |          |0x47    |0x4A       |          |
     |Navicat Premium      |0x65      |        |0x66       |0x67      |
     |Navicat MySQL        |0x68      |0x69    |0x6A       |0x6B      |
     |Navicat PostgreSQL   |0x6C      |0x6D    |0x6E       |0x6F      |
     |Navicat Oracle       |0x70      |0x71    |0x72       |0x73      |
     |Navicat SQL Server   |0x74      |0x75    |0x76       |0x77      |
     |Navicat SQLite       |0x78      |0x79    |0x7A       |0x7B      |
     |Navicat MariaDB      |0x7C      |0x7D    |0x7E       |0x7F      |
     |Navicat MongoDB      |0x80      |0x81    |0x82       |          |

  5. __data[8]__ 的高4位代表 __版本号__。低4位未知，但可以用来延长激活期限，可取的值有`0000`和`0001`。

     例如：

     对于 __Navicat 12__: 高4位必须是`1100`，为`12`的二进制形式。  
     对于 __Navicat 11__: 高4位必须是`1011`，为`11`的二进制形式。  

  6. __data[9]__ 目前暂未知，但如果你想要 __not-for-resale license__ 的话可以设成`0xFD`、`0xFC`或`0xFB`。

     根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知：

     * `0xFB`是 __Not-For-Resale-30-days__ license.  
     * `0xFC`是 __Not-For-Resale-90-days__ license.  
     * `0xFD`是 __Not-For-Resale-365-days__ license.  
     * `0xFE`是 __Not-For-Resale__ license.  
     * `0xFF`是 __Site__ license.  

  之后Navicat使用 __ECB__ 模式的 __DES__ 算法来加密 __data[10]__ 的后8字节，也就是 __data[2]__ 到 __data[9]__ 的部分。

  相应的DES密钥为：

  ```cpp
  const uint8_t DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
  ```

  之后使用Base32编码 __data[10]__，其中编码表改为：

  ```cpp
  // Thanks for discoveries from @Wizr, issue #10
  char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
  ```

  编码之后你应该会得到一个16字节长的字符串，并且以"NAV"打头。

  将16字节的字符串分成4个4字节的小块，然后用`"-"`连接就可以得到 __序列号__。

## 2. 激活过程

1. 检查用户输入的 __序列号__ 是否合法。

2. 在用户点击了`激活`按钮之后，Navicat会先尝试在线激活。如果失败，用户可以选择离线激活。

3. Navicat会使用用户输入的 __序列号__ 以及从用户电脑收集来的信息生成 __离线激活请求信息__，然后用 __Navicat激活公钥__ 加密，并将密文用Base64编码，最后得到 __请求码__。

4. 正常流程下，__请求码__ 应该通过可联网的电脑发送给Navicat的官方激活服务器。之后Navicat的官方激活服务器会返回一个合法的 __激活码__。

但现在我们使用注册机来扮演官方激活服务器的角色，只是Navicat软件里的激活公钥得换成自己的公钥：

1. 根据 __请求码__, 获得`"DI"`值和`"K"`值。

2. 用`"K"`值、用户名、组织名和`"DI"`值填写 __离线激活回复信息__。

3. 用自己的2048位RSA私钥加密 __离线激活回复信息__，你将会得到256字节的密文。

4. 用Base64编码这256字节的密文，就可以得到 __激活码__。

5. 在Navicat软件中填入 __激活码__ 即可完成离线激活。
